/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.formserver.vmmanager.service;

import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.CommonDtmCollection;
import com.inspur.edp.cef.designtime.api.operation.CommonDetermination;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.das.commonmodel.entity.GspCommonElement;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import com.inspur.edp.formserver.viewmodel.action.MappedCdpAction;
import com.inspur.edp.formserver.viewmodel.action.MappedCdpActionBase;
import com.inspur.edp.formserver.viewmodel.action.ViewModelAction;
import com.inspur.edp.formserver.viewmodel.action.ViewModelActionType;
import com.inspur.edp.formserver.viewmodel.collection.VMActionCollection;
import com.inspur.edp.formserver.viewmodel.common.ValueHelpConfig;
import com.inspur.edp.formserver.viewmodel.exception.ViewModelException;
import com.inspur.edp.formserver.vmmanager.exception.VoManagerErrorCodes;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataReference;
import com.inspur.edp.lcm.metadata.api.service.RefCommonService;
import com.inspur.edp.lcm.metadata.spi.IMetadataReferenceManager;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MetadataReferenceService implements IMetadataReferenceManager {

    private HashMap<String, String> map = new HashMap<String, String>();
    private String errorCode = "MetadataReferenceService";
    //TODO 临时添加
    private String errorToken = "#GSPBefError# ";

    private void buildMetadataReference(GspMetadata metadata) {
        if (metadata.getRefs() == null) {
            metadata.setRefs(new ArrayList<MetadataReference>());
        }
        map.clear();
        RefCommonService refService = SpringBeanUtils.getBean(RefCommonService.class);
        GspViewModel vm = (GspViewModel) metadata.getContent();
        addAssoVirtualFieldBeReference(vm, metadata, refService);
        addActionReference(vm, metadata, refService);
        // 虚拟vo无依赖
        if (vm.getIsVirtual()) {
            return;
        }

        // ① vm.Mapping
        String beId = vm.getMapping().getTargetMetadataId();
        addMappingBeReference(beId, metadata, refService);

        // ② obj
        dealObjectReference(vm.getMainObject(), metadata, refService);
        //
        addActionReference(vm, metadata, refService);
    }

    private void addActionReference(GspViewModel vm, GspMetadata metadata, RefCommonService refService) {
        dealActions(vm, metadata, refService);
        dealDataExtendInfo(vm, metadata, refService);
        dealVariables(vm, metadata, refService);
        dealHelpConfig(vm, metadata, refService);
    }

    private void dealVariables(GspViewModel vm, GspMetadata metadata, RefCommonService refService) {
        if (vm.getVariables() == null)
            return;
        if (vm.getVariables().getDtmAfterModify() == null || vm.getVariables().getDtmAfterModify().size() == 0)
            return;
        CommonDtmCollection dtmAfterModify = vm.getVariables().getDtmAfterModify();
        for (CommonDetermination commonDetermination : dtmAfterModify) {
            addMappingBeReference(commonDetermination.getComponentId(), metadata, refService);
        }
    }

    private void dealDataExtendInfo(GspViewModel vm, GspMetadata metadata, RefCommonService refService) {
        if (vm.getDataExtendInfo() == null)
            return;
        VMActionCollection beforeCreateActions = vm.getDataExtendInfo().getBeforeCreateActions();
        addDataExtendInfo(beforeCreateActions, vm, metadata, refService);

        VMActionCollection createActions = vm.getDataExtendInfo().getCreateActions();
        addDataExtendInfo(createActions, vm, metadata, refService);

        VMActionCollection afterCreateActions = vm.getDataExtendInfo().getAfterCreateActions();
        addDataExtendInfo(afterCreateActions, vm, metadata, refService);


        VMActionCollection beforeRetrieveActions = vm.getDataExtendInfo().getBeforeRetrieveActions();
        addDataExtendInfo(beforeRetrieveActions, vm, metadata, refService);

        VMActionCollection retrieveActions = vm.getDataExtendInfo().getRetrieveActions();
        addDataExtendInfo(retrieveActions, vm, metadata, refService);

        VMActionCollection afterRetrieveActions = vm.getDataExtendInfo().getAfterRetrieveActions();
        addDataExtendInfo(afterRetrieveActions, vm, metadata, refService);


        VMActionCollection beforeQueryActions = vm.getDataExtendInfo().getBeforeQueryActions();
        addDataExtendInfo(beforeQueryActions, vm, metadata, refService);

        VMActionCollection queryActions = vm.getDataExtendInfo().getQueryActions();
        addDataExtendInfo(queryActions, vm, metadata, refService);

        VMActionCollection afterQueryActions = vm.getDataExtendInfo().getAfterQueryActions();
        addDataExtendInfo(afterQueryActions, vm, metadata, refService);


        VMActionCollection beforeModifyActions = vm.getDataExtendInfo().getBeforeModifyActions();
        addDataExtendInfo(beforeModifyActions, vm, metadata, refService);

        VMActionCollection modifyActions = vm.getDataExtendInfo().getModifyActions();
        addDataExtendInfo(modifyActions, vm, metadata, refService);

        VMActionCollection afterModifyActions = vm.getDataExtendInfo().getAfterModifyActions();
        addDataExtendInfo(afterModifyActions, vm, metadata, refService);


        VMActionCollection beforeDeleteActions = vm.getDataExtendInfo().getBeforeDeleteActions();
        addDataExtendInfo(beforeDeleteActions, vm, metadata, refService);

        VMActionCollection deleteActions = vm.getDataExtendInfo().getDeleteActions();
        addDataExtendInfo(deleteActions, vm, metadata, refService);

        VMActionCollection afterDeleteActions = vm.getDataExtendInfo().getAfterDeleteActions();
        addDataExtendInfo(afterDeleteActions, vm, metadata, refService);


        VMActionCollection beforeMultiDeleteActions = vm.getDataExtendInfo().getBeforeMultiDeleteActions();
        addDataExtendInfo(beforeMultiDeleteActions, vm, metadata, refService);

        VMActionCollection multiDeleteActions = vm.getDataExtendInfo().getMultiDeleteActions();
        addDataExtendInfo(multiDeleteActions, vm, metadata, refService);

        VMActionCollection afterMultiDeleteActions = vm.getDataExtendInfo().getAfterMultiDeleteActions();
        addDataExtendInfo(afterMultiDeleteActions, vm, metadata, refService);


        VMActionCollection dataMappingActions = vm.getDataExtendInfo().getDataMappingActions();
        addDataExtendInfo(dataMappingActions, vm, metadata, refService);

        VMActionCollection dataReversalMappingActions = vm.getDataExtendInfo().getDataReversalMappingActions();
        addDataExtendInfo(dataReversalMappingActions, vm, metadata, refService);


        VMActionCollection changesetReversalMappingActions = vm.getDataExtendInfo().getChangesetReversalMappingActions();
        addDataExtendInfo(changesetReversalMappingActions, vm, metadata, refService);

        VMActionCollection changesetMappingActions = vm.getDataExtendInfo().getChangesetMappingActions();
        addDataExtendInfo(changesetMappingActions, vm, metadata, refService);


        VMActionCollection beforeSaveActions = vm.getDataExtendInfo().getBeforeSaveActions();
        addDataExtendInfo(beforeSaveActions, vm, metadata, refService);

        VMActionCollection saveActions = vm.getDataExtendInfo().getSaveActions();
        addDataExtendInfo(saveActions, vm, metadata, refService);

        VMActionCollection afterSaveActions = vm.getDataExtendInfo().getAfterSaveActions();
        addDataExtendInfo(afterSaveActions, vm, metadata, refService);


    }

    private void addDataExtendInfo(VMActionCollection actions, GspViewModel vm, GspMetadata metadata, RefCommonService refService) {
        if (actions == null || actions.size() == 0)
            return;
        for (ViewModelAction action : actions) {
            if (action instanceof MappedCdpActionBase)
                addMappingBeReference(((MappedCdpActionBase) action).getComponentEntityId(), metadata, refService);
        }
    }

    private void dealHelpConfig(GspViewModel vm, GspMetadata metadata, RefCommonService refService) {
        if (vm.getValueHelpConfigs() == null || vm.getValueHelpConfigs().size() == 0)
            return;
        for (ValueHelpConfig valueHelpConfig : vm.getValueHelpConfigs()) {
            addMappingBeReference(valueHelpConfig.getHelperId(), metadata, refService);
        }
    }

    private void dealActions(GspViewModel vm, GspMetadata metadata, RefCommonService refService) {
        if (vm.getActions() == null || vm.getActions().getCount() == 0)
            return;
        for (ViewModelAction action : vm.getActions()) {
            if (action.getType() == ViewModelActionType.VMAction) {
                if (action instanceof MappedCdpAction) {
                    buildCompReference((MappedCdpAction) action, metadata, refService);
                } else {
                    throw new ViewModelException(VoManagerErrorCodes.GSP_VIEWOBJECT_MANAGER_0039, null,
                            vm.getCode(), vm.getName(), action.getCode(), action.getName(), action.getType().toString());
                }
            }
        }
    }

    private void addAssoVirtualFieldBeReference(GspViewModel viewModel, GspMetadata metadata, RefCommonService refService) {
        List<IGspCommonElement> elementList = viewModel.getAllElementList(false);
        for (IGspCommonElement element : elementList) {
            if (!element.getIsVirtual()) {
                continue;
            }
            if (element.getIsUdt()) {
                addMappingBeReference(element.getUdtID(), metadata, refService);
            } else if (element.getHasAssociation() && element.getChildAssociations().size() >= 0) {
                String refBeId = ((GspCommonElement) element).getChildAssociations().get(0).getRefModelID();
                addMappingBeReference(refBeId, metadata, refService);
            }
        }
    }


    private void dealObjectReference(GspViewObject obj, GspMetadata metadata, RefCommonService refService) {
        if (obj.getIsVirtual()) {
            return;
        }
        // ① mapping
        addMappingBeReference(obj.getMapping().getTargetMetadataId(), metadata, refService);
        // ① Elemnet
        if (obj.getContainElements() != null && obj.getContainElements().getCount() > 0) {
            for (IGspCommonField ele : obj.getContainElements()) {
                dealElementReference((GspViewModelElement) ele, metadata, refService);
            }
        }

        // ② Child
        if (obj.getContainChildObjects() != null && obj.getContainChildObjects().getCount() > 0) {
            for (IGspCommonObject childObj : obj.getContainChildObjects()) {
                dealObjectReference((GspViewObject) childObj, metadata, refService);
            }
        }
    }

    private void dealElementReference(GspViewModelElement element, GspMetadata metadata, RefCommonService refService) {
        if (element.getIsUdt()) {
            addMappingBeReference(element.getUdtID(), metadata, refService);
        } else if (element.getHasAssociation() && element.getChildAssociations().size() >= 0) {
            String refBeId = ((GspCommonElement) element).getChildAssociations().get(0).getRefModelID();
            addMappingBeReference(refBeId, metadata, refService);
        }
        //① mapping
        if (element.getIsVirtual())
            return;
        addMappingBeReference(element.getMapping().getTargetMetadataId(), metadata, refService);

    }

    public void buildCompReference(MappedCdpAction operation, GspMetadata metadata, RefCommonService refService) {
        addMappingBeReference(operation.getComponentEntityId(), metadata, refService);
    }

    private void addMappingBeReference(String refBeId, GspMetadata metadata, RefCommonService refService) {
        if (refBeId == null || "".equals(refBeId) || map.containsKey(refBeId)) {
            return;
        }
        map.put(refBeId, refBeId);
        buildReference(refBeId, metadata, refService);
    }

    private void buildReference(String refMetadataId, GspMetadata metadata, RefCommonService refService) {
        if (refMetadataId == null || "".equals(refMetadataId)) {
            return;
        }
        for (MetadataReference metadataReference : metadata.getRefs()) {
            if (refMetadataId.equals(metadataReference.getDependentMetadata().getId())) {
                return;
            }
        }
        GspMetadata refMetaData = refService.getRefMetadata(refMetadataId);
        if (refMetaData == null) {
            StringBuilder builder = new StringBuilder();
            GspViewModel viewModel = (GspViewModel) metadata.getContent();
            throw new ViewModelException(VoManagerErrorCodes.GSP_VIEWOBJECT_MANAGER_0040, null, viewModel.getCode(), viewModel.getName(), refMetadataId);
        }
        MetadataReference metadataReference = new MetadataReference();
        metadataReference.setMetadata(metadata.getHeader());
        metadataReference.setDependentMetadata(refMetaData.getHeader());
        metadata.getRefs().add(metadataReference);
    }

    public List<MetadataReference> getConstraint(GspMetadata metadata) {
        if (metadata.getRefs() != null) {
            metadata.getRefs().clear();
        }
        buildMetadataReference(metadata);
        List<MetadataReference> list = new ArrayList<>();
        if (metadata.getRefs() != null && metadata.getRefs().size() > 0) {
            for (MetadataReference item : metadata.getRefs()) {
                list.add(item);
            }
        }
        return list;
    }
}
